package srzy.socket

import scala.actors.Actor
import scala.actors.Actor._
import scala.actors.TIMEOUT
import java.net.SocketTimeoutException
import srzy.common.Logging
import srzy.common.Config
import srzy.diameter._
import srzy.module.CaseDealContext

object HbHId {
  var i = 2000L
  def apply() = {
    i += 1
    i
  }
}

object EtEId {
  var i = 2000L
  def apply() = {
    i += 1
    i
  }
}

class DCCReceActor(dccMsgActor: DCCMsgActor) extends Actor with Logging {

  override def act = {
    while (true) {
      try {
        //        logger.debug("read from socket")
        val arr: Array[Byte] = dccMsgActor.socketConn.read
        //
        val node = arr.toDCCNode
        node.attributes.apply("name").head.text match {
          case "DWR" => {
            val (hbhId, eteId) = srzy.diameter.getHbHIdAndEtEIdFromHead(arr)
            val llll = List(("HbH-id", hbhId), ("EtE-id", eteId))
            val bytes = DCCSocketConn.DWAMsg.toBytes(llll)
            //            logger.debug("send :\n" + bytes.toDCCCompareLog)
            dccMsgActor.socketConn.write(bytes)
            logger.info("DWR Msg")
          }
          case "CEA" => {
            logger.error("CEA Msg recived")
          }
          case "RAR" => {
            val sid = getSessionId(node)
            dccMsgActor ! ('rece, sid, arr.toDCCCompareLog)
            //            logger.debug("rece :\n" + arr.toFormatStr)
            logger.debug("rece :\n" + arr.toDCCCompareLog)

            val (hbhId, eteId) = srzy.diameter.getHbHIdAndEtEIdFromHead(arr)
            val llll = List(("HbH-id", hbhId), ("EtE-id", eteId))

            val bytes = DCCSocketConn.RAAMsg(sid).toBytes(llll)
            //            logger.debug("send :\n" + bytes.toDCCCompareLog)
            dccMsgActor.socketConn.write(bytes)
          }
          case "ASR" => {
            val sid = getSessionId(node)
            dccMsgActor ! ('rece, sid, arr.toDCCCompareLog)
            //            logger.debug("rece :\n" + arr.toFormatStr)
            logger.debug("rece :\n" + arr.toDCCCompareLog)

            val (hbhId, eteId) = srzy.diameter.getHbHIdAndEtEIdFromHead(arr)
            val llll = List(("HbH-id", hbhId), ("EtE-id", eteId))
            val bytes = DCCSocketConn.ASAMsg(sid).toBytes(llll)
            //            logger.debug("send :\n" + bytes.toDCCCompareLog)
            dccMsgActor.socketConn.write(bytes)
          }
          case "CCA" => {
            val sid = getSessionId(node)
            dccMsgActor ! ('rece, sid, arr.toDCCCompareLog)
            //            logger.debug("rece :\n" + arr.toFormatStr)
            logger.debug("rece :\n" + arr.toDCCCompareLog)
          }
          case "NCR" => {
            logger.debug("NCR ignore")
          }
          case "NCA" => {
            logger.debug("NCA ignore")
          }
          case s: String => {
            logger.error("not considered type : " + s)
            logger.error("rece :\n" + node)
          }
          case _ => {
            logger.error("type illegal")
            logger.error("rece :\n" + node)
          }
        }
      } catch {
        case ste: SocketTimeoutException =>
        //          logger.debug("wait socket connect")
      }
    }
  }

  override def exceptionHandler = {
    case ioe: java.io.IOException => {
      logger.error(ioe.getMessage(), ioe)
      ExceptionHandle.smDCCSocketExceptionDeal
    }
    case e: Exception => logger.error(e.getMessage(), e)
  }
}

class DCCMsgActor(getDCCSocket: => java.net.Socket) extends Actor with Logging {

  val socketConn: DCCSocketConn = new DCCSocketConn {
    val socket = getDCCSocket
    val outStream = socket.getOutputStream()
    val inStream = socket.getInputStream()
  }

  var oc: scala.actors.OutputChannel[Any] = null
  var DCCReceActor: DCCReceActor = null

  val clq = new java.util.concurrent.LinkedBlockingQueue[String](500)

  //  var sessionId = ""
  var requestTime: Long = 0
  var timeoutTime: Long = 10000
  val defaultTimeoutTime: Long = 10000

  override def act = {
    loop {
      reactWithin(1000) {
        case sendMsg: scala.xml.Node => {
          if (oc == null) {
            //            sessionId = getSessionId(sendMsg)
            val llll = List(("HbH-id", srzy.socket.HbHId()), ("EtE-id", srzy.socket.EtEId()))
            val arr = sendMsg.toBytes(llll)
            //          logger.debug("send :\n" + arr.toFormatStr)
            logger.debug("send :\n" + arr.toDCCCompareLog)
            socketConn.write(arr)
            oc = sender
          } else {
            logger.error("The message has not been returned, please waiting for the return")
          }
        }

        case 'getRece => {
          if (clq.peek() == null) {
            oc = sender
            requestTime = System.currentTimeMillis()
          } else {
            sender ! clq.poll()
          }
        }

        case ('timeout, i: Int) =>
          logger.debug("set dcc timeout time : " + i)
          timeoutTime = i

        case ('rece, sId: String, receMsg: String) => {
          timeoutTime = defaultTimeoutTime
          if (CaseDealContext.sessionId == sId) {
            if (oc != null) {
              oc ! receMsg
              oc = null
            } else {
              clq.offer(receMsg)
            }
          } else {
            logger.error("sessionId err discard msg")
            logger.error("sId :" + sId)
            logger.error("sessionId :" + CaseDealContext.sessionId)
            logger.error("receMsg :\n" + receMsg)
          }
        }
        case 'exit => {
          socketConn.close
          sender ! 'ok
          exit()
        }
        case TIMEOUT => {
          if (oc != null) {
            if (System.currentTimeMillis() - requestTime >= timeoutTime) {
              oc ! ""
              oc = null
              //              sessionId = ""
              logger.error("no dcc msg recevice!!!")
            } else {
              val i = (System.currentTimeMillis() - requestTime).toDouble / 1000
              val i2 = new java.math.BigDecimal(i).setScale(0, java.math.BigDecimal.ROUND_HALF_UP)
              logger.debug("waiting for dcc msg recevice : " + i2 + " s")
            }
          }
        }
        case a: Any => logger.error("unknown pattern " + a.toString)
      }
    }
  }

  def authentication = {
    logger.debug("Authentication start")
    val CERMsg = <command name="CER">
                   <avp name="Origin-Host" value="sm-all-test"></avp>
                   <avp name="Origin-Realm" value="chinaunicom.com"></avp>
                   <avp name="Host-IP-Address" value="0x000100000000"></avp>
                   <avp name="Vendor-Id" value="11"></avp>
                   <avp name="Product-Name" value="HP Cx Interface"></avp>
                   <avp name="Origin-State-Id" value="62"></avp>
                   <avp name="Supported-Vendor-Id" value="10415"></avp>
                   <avp name="Auth-Application-Id" value="4"></avp>
                   <avp name="Acct-Application-Id" value="0"></avp>
                   <avp name="Firmware-Revision" value="1"></avp>
                 </command>
    val llll = List(("HbH-id", HbHId()), ("EtE-id", EtEId()))
    val arr = CERMsg.toBytes(llll)
    logger.debug("send :\n" + arr.toFormatStr)
    logger.debug("send :\n" + arr.toDCCCompareLog)
    socketConn.write(arr)

    val arr2: Array[Byte] = socketConn.read
    logger.debug("rece :\n" + arr2.toFormatStr)
    logger.debug("rece :\n" + arr2.toDCCCompareLog)
    logger.info("Authentication success")
  }

  override def start(): Actor = synchronized {
    super.start()
    try {
      this.authentication
    } catch {
      case e: Exception => {
        logger.error("dcc authentication fail", e)
        ExceptionHandle.SMAuthenticationExceptionDeal

      }
    }
    DCCReceActor = new DCCReceActor(DCCMsgActor.this)
    DCCReceActor.start
    DCCMsgActor.this
  }

  override def exceptionHandler = {
    case ioe: java.io.IOException => {
      logger.error(ioe.getMessage(), ioe)
      ExceptionHandle.smDCCSocketExceptionDeal
    }
    case e: Exception => logger.error(e.getMessage(), e)
  }
}

object DCCMsgActor extends Logging {

  val ip = Config.getValue("root", "config", "SM_IP")
  val port = Integer.parseInt(Config.getValue("root", "config", "SM_Port"))

  def getDCCSocket = try {
    SocketConn.connect(ip, port)
  } catch {
    case e: Exception =>
      logger.error(e.getMessage(), e)
      ExceptionHandle.smDCCSocketConnectExceptionDeal
  }

  val actor = new DCCMsgActor(getDCCSocket)

  def apply() = actor

}